#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>
#include <vector>
#include <string>
#include <iostream>
#include <memory>

class img_process
{
private:
    cv::VideoCapture cap_;      // 视频捕获对象
    cv::Mat current_frame_;     // 当前帧
    cv::Mat processed_frame_;   // 处理后的帧
    cv::Mat edges_;             // 边缘图像
    bool video_opened_ = false; // 输入源是否成功打开
    bool frame_ready_ = false;  // 帧是否准备好
    // 三条测量线的Y坐标
    int measurement_y1_ = 160;
    int measurement_y2_ = 240;
    int measurement_y3_ = 320;
    // 每条线对应的历史最大宽度及对应Y坐标
    int max_width1_ = 0, max_width_y1_ = 0;
    int max_width2_ = 0, max_width_y2_ = 0;
    int max_width3_ = 0, max_width_y3_ = 0;
    const std::string window_name_ = "Video Viewer";
    std::string input_source_; // 输入源：空字符串表示摄像头，否则为视频路径
    int cam_index_ = 0;        // 摄像头索引（默认0）
    int width_ = 640;          // 分辨率宽度
    int height_ = 480;         // 分辨率高度

    // 滑动条回调函数，处理三条线位置更新
    static void trackbarCallback(int, void *userdata)
    {
        img_process *node = static_cast<img_process *>(userdata);
        if (node->frame_ready_)
        {
            node->updateMeasurementLine(node->edges_, 1);
            node->updateMeasurementLine(node->edges_, 2);
            node->updateMeasurementLine(node->edges_, 3);
        }
    }

public:
    // 构造函数：支持摄像头索引或视频路径
    img_process(const std::string &source, int cam_idx, int width, int height) :
        input_source_(source), cam_index_(cam_idx), width_(width), height_(height)
    {
        openInputSource();

        // 创建窗口和三条滑动条
        cv::namedWindow(window_name_, cv::WINDOW_NORMAL);
        cv::createTrackbar("Y1 Position", window_name_, &measurement_y1_, height_ - 1, trackbarCallback, this);
        cv::createTrackbar("Y2 Position", window_name_, &measurement_y2_, height_ - 1, trackbarCallback, this);
        cv::createTrackbar("Y3 Position", window_name_, &measurement_y3_, height_ - 1, trackbarCallback, this);

        std::cout << "按 'q' 退出，'c' 切换摄像头，'v' 切换视频，'r' 重置最大宽度" << std::endl;
    }

    ~img_process()
    {
        cv::destroyAllWindows();
        if (cap_.isOpened()) cap_.release();
    }

    // 打开输入源（摄像头或视频文件）
    void openInputSource()
    {
        if (cap_.isOpened()) cap_.release();

        if (input_source_.empty())
        {
            cap_.open(cam_index_);
            std::cout << "尝试打开摄像头 " << cam_index_ << std::endl;
        } else
        {
            cap_.open(input_source_);
            std::cout << "尝试打开视频文件: " << input_source_ << std::endl;
        }

        if (!cap_.isOpened())
        {
            std::cerr << "无法打开输入源！" << std::endl;
            video_opened_ = false;
            return;
        }

        cap_.set(cv::CAP_PROP_FRAME_WIDTH, width_);
        cap_.set(cv::CAP_PROP_FRAME_HEIGHT, height_);
        std::cout << "分辨率设置为: " << width_ << "x" << height_ << std::endl;

        video_opened_ = true;
        frame_ready_ = false;
        // 初始化三条线位置
        measurement_y1_ = height_ / 3;
        measurement_y2_ = height_ / 2;
        measurement_y3_ = height_ * 2 / 3;
        cv::setTrackbarPos("Y1 Position", window_name_, measurement_y1_);
        cv::setTrackbarPos("Y2 Position", window_name_, measurement_y2_);
        cv::setTrackbarPos("Y3 Position", window_name_, measurement_y3_);
    }

    // 获取当前帧高度
    int getFrameHeight() const
    {
        return video_opened_ ? static_cast<int>(cap_.get(cv::CAP_PROP_FRAME_HEIGHT)) : height_;
    }

    // 获取当前帧宽度
    int getFrameWidth() const
    {
        return video_opened_ ? static_cast<int>(cap_.get(cv::CAP_PROP_FRAME_WIDTH)) : width_;
    }

    // 主循环处理
    void run()
    {
        while (true)
        {
            if (!video_opened_) break;

            if (cap_.read(current_frame_))
            {
                frame_ready_ = true;
                processFrame();
                cv::imshow(window_name_, processed_frame_);
            } else
            {
                if (!input_source_.empty())
                {
                    cap_.set(cv::CAP_PROP_POS_FRAMES, 0);
                    std::cout << "视频循环播放..." << std::endl;
                } else
                {
                    std::cout << "摄像头读取失败！" << std::endl;
                }
            }

            int key = cv::waitKey(30);
            if (key == 'q' || key == 'Q')
            {
                break;
            } else if (key == 'c' || key == 'C')
            {
                switchToCamera(0);
            } else if (key == 'v' || key == 'V')
            {
                switchToVideo();
            } else if (key == 'r' || key == 'R')
            {
                max_width1_ = max_width2_ = max_width3_ = 0;
                max_width_y1_ = max_width_y2_ = max_width_y3_ = 0;
                std::cout << "已重置历史最大宽度" << std::endl;
            }
        }
    }

    // 切换到摄像头
    void switchToCamera(int cam_idx)
    {
        input_source_.clear();
        cam_index_ = cam_idx;
        openInputSource();
    }

    // 切换到视频文件（运行时输入路径）
    void switchToVideo()
    {
        std::string path;
        std::cout << "请输入视频文件路径: ";
        std::getline(std::cin, path);
        input_source_ = path;
        openInputSource();
    }

    // 处理帧（边缘检测等）
    void processFrame()
    {
        processed_frame_ = current_frame_.clone();

        // HSV颜色过滤（绿色示例，可根据实际需求调整）
        cv::Mat hsv, green_mask;
        cv::cvtColor(processed_frame_, hsv, cv::COLOR_BGR2HSV);
        cv::inRange(hsv, cv::Scalar(35, 50, 50), cv::Scalar(85, 255, 255), green_mask);

        // 形态学闭运算
        cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(5, 5));
        cv::morphologyEx(green_mask, green_mask, cv::MORPH_CLOSE, kernel);

        // 边缘检测
        cv::Canny(green_mask, edges_, 50, 150);

        // 分别更新三条测量线
        updateMeasurementLine(edges_, 1);
        updateMeasurementLine(edges_, 2);
        updateMeasurementLine(edges_, 3);
    }

    // 更新单条测量线和宽度显示（lineIdx 为 1、2、3，区分三条线）
    void updateMeasurementLine(const cv::Mat &edges, int lineIdx)
    {
        int measurement_y = 0;
        int &max_width = (lineIdx == 1) ? max_width1_ : (lineIdx == 2) ? max_width2_ :
                                                                         max_width3_;
        int &max_width_y = (lineIdx == 1) ? max_width_y1_ : (lineIdx == 2) ? max_width_y2_ :
                                                                             max_width_y3_;

        // 根据线索引获取Y坐标
        if (lineIdx == 1)
            measurement_y = measurement_y1_;
        else if (lineIdx == 2)
            measurement_y = measurement_y2_;
        else if (lineIdx == 3)
            measurement_y = measurement_y3_;

        // 绘制当前测量线（绿色）
        cv::line(processed_frame_,
                 cv::Point(0, measurement_y),
                 cv::Point(processed_frame_.cols, measurement_y),
                 cv::Scalar(0, 255, 0), 2);

        // 检测当前Y线的零件宽度
        if (measurement_y < 0 || measurement_y >= edges.rows) return;
        const uchar *line_data = edges.ptr<uchar>(measurement_y);
        int left = -1, right = -1;

        for (int x = 0; x < edges.cols; x++)
        {
            if (line_data[x] > 0)
            {
                left = x;
                break;
            }
        }
        for (int x = edges.cols - 1; x >= 0; x--)
        {
            if (line_data[x] > 0)
            {
                right = x;
                break;
            }
        }

        // 计算宽度并更新历史最大
        if (left != -1 && right != -1 && left < right)
        {
            int width = right - left;
            if (width > max_width)
            {
                max_width = width;
                max_width_y = measurement_y;
            }
            // 当前宽度：显示在测量线左侧
            cv::putText(processed_frame_,
                        "Current Width: " + std::to_string(width) + "px",
                        cv::Point(10, measurement_y - 20),
                        cv::FONT_HERSHEY_SIMPLEX, 0.7, cv::Scalar(0, 0, 255), 2);
            cv::circle(processed_frame_, cv::Point(left, measurement_y), 5, cv::Scalar(0, 0, 255), -1);
            cv::circle(processed_frame_, cv::Point(right, measurement_y), 5, cv::Scalar(0, 0, 255), -1);
        } else
        {
            cv::putText(processed_frame_,
                        "edge no detect",
                        cv::Point(10, measurement_y - 20),
                        cv::FONT_HERSHEY_SIMPLEX, 0.7, cv::Scalar(0, 0, 255), 2);
        }

        // 历史最大宽度：固定显示在左下角（按行区分，避免重叠）
        int base_y = height_ - 30; // 左下角基准Y坐标
        int offset = 30;           // 行间距
        if (lineIdx == 1)
        {
            if (max_width > 0)
            {
                std::string max_info = "Max1: " + std::to_string(max_width) + "px at Y=" + std::to_string(max_width_y);
                cv::putText(processed_frame_, max_info,
                            cv::Point(10, base_y),
                            cv::FONT_HERSHEY_SIMPLEX, 0.7, cv::Scalar(255, 0, 0), 2);
            }
        } else if (lineIdx == 2)
        {
            if (max_width > 0)
            {
                std::string max_info = "Max2: " + std::to_string(max_width) + "px at Y=" + std::to_string(max_width_y);
                cv::putText(processed_frame_, max_info,
                            cv::Point(10, base_y - offset),
                            cv::FONT_HERSHEY_SIMPLEX, 0.7, cv::Scalar(255, 0, 0), 2);
            }
        } else if (lineIdx == 3)
        {
            if (max_width > 0)
            {
                std::string max_info = "Max3: " + std::to_string(max_width) + "px at Y=" + std::to_string(max_width_y);
                cv::putText(processed_frame_, max_info,
                            cv::Point(10, base_y - 2 * offset),
                            cv::FONT_HERSHEY_SIMPLEX, 0.7, cv::Scalar(255, 0, 0), 2);
            }
        }

        // 输入源和分辨率信息：固定显示在左上角（仅第一条线处理，避免重复）
        if (lineIdx == 1)
        {
            std::string info = input_source_.empty() ?
                                   "Input: Camera " + std::to_string(cam_index_) :
                                   "Input: " + input_source_;
            cv::putText(processed_frame_, info, cv::Point(10, 30), cv::FONT_HERSHEY_SIMPLEX, 0.7, cv::Scalar(0, 255, 0), 2);
            cv::putText(processed_frame_,
                        "Resolution: " + std::to_string(width_) + "x" + std::to_string(height_),
                        cv::Point(10, 60),
                        cv::FONT_HERSHEY_SIMPLEX, 0.7, cv::Scalar(0, 255, 0), 2);
        }
    }
};

int main(int argc, char const *argv[])
{
    std::string input_source;
    int cam_idx = 2;
    int width = 640, height = 480;

    if (argc > 1)
    {
        input_source = argv[1];
    }

    auto processor = std::make_shared<img_process>(input_source, cam_idx, width, height);
    processor->run();

    return 0;
}